package org.vacoor.xqq.ui.comp.tree;

import org.vacoor.xqq.core.bean.IdentifiableAndObservableData;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Vector;

/**
 * 唯一标识化且可被监听属性变化的树节点
 * 对该类添加的属性监听器, 同时会收到节点数据变化通知
 * 节点数据的改变会作为节点改变在属性名称前追加 "data." 后转发
 * <p/>
 * <p/>
 * 该类及子类在属性/数据改变时如果希望观察者获知该变化,
 * 则应该调用 {@link #firePropertyChange(String, Object, Object)}
 * <p/>
 *
 * @author: Vacoor
 */
public abstract class IdentifiableAndObservableDataNode<D extends IdentifiableAndObservableData> extends DefaultMutableTreeNode {
    private final long id;
    private final Object lock = new Object();
    private PropertyChangeSupport changeSupport;
    private DataChangeTransponder transponder;

    public IdentifiableAndObservableDataNode(long id) {
        this.id = id;
    }

    public IdentifiableAndObservableDataNode(D data) {
        this(data.getId(), data);
    }

    public IdentifiableAndObservableDataNode(long id, D data) {
        this.id = id;
        setData(data);
    }

    public long getId() {
        return id;
    }

    public void setData(D data) {
        if (data != null) {
            ensureTransponder();

            /** 这里一定要把监听移除, 否则依然会派发 propertyChangeEvent,
             * 晕了, 忘记移除影响了 {@link CategoryNode.ContactStatusMonitor}
             * */
            if (userObject != null) {
                ((D) userObject).removePropertyChangeListener(transponder);
            }
            data.addPropertyChangeListener(transponder);
        }
        Object old = getUserObject();
        setUserObject(data);
        firePropertyChange("data", old, data);
    }

    public D getData() {
        return (D) getUserObject();
    }

    @Override
    @Deprecated
    public void setUserObject(Object userObject) {
        Object oldValue = this.userObject;
        super.setUserObject(userObject);
        firePropertyChange("userObject", oldValue, userObject);
    }

    // 所有添加方法都是调用该方法
    @Override
    public void insert(MutableTreeNode newChild, int childIndex) {
        Vector<TreeNode> old = children != null ? (Vector<TreeNode>) children.clone() : null;
        super.insert(newChild, childIndex);
        if (old == null) {
            firePropertyChange("children", old, children);
        } else {
            fireIndexedPropertyChange("children", childIndex, old, children);
        }
    }

    // 所有的 remove 都调用的该方法, 只重写改方法即可
    @Override
    public void remove(int childIndex) {
        Vector<TreeNode> old = children != null ? (Vector<TreeNode>) children.clone() : null;
        super.remove(childIndex);
        if (old == null) {
            firePropertyChange("children", old, children);
        } else {
            fireIndexedPropertyChange("children", childIndex, old, children);
        }
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        ensurePropertyChangeSupport();
        changeSupport.addPropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String propName, PropertyChangeListener listener) {
        ensurePropertyChangeSupport();
        changeSupport.addPropertyChangeListener(propName, listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        if (changeSupport == null) {
            return;
        }
        changeSupport.removePropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(String propName, PropertyChangeListener listener) {
        if (changeSupport == null) {
            return;
        }
        changeSupport.removePropertyChangeListener(propName, listener);
    }

    public PropertyChangeListener[] getPropertyChangeListeners() {
        PropertyChangeListener[] listeners = new PropertyChangeListener[0];
        if (changeSupport != null) {
            listeners = changeSupport.getPropertyChangeListeners();
        }
        return listeners;
    }

    public PropertyChangeListener[] getPropertyChangeListeners(String propName) {
        PropertyChangeListener[] listeners = new PropertyChangeListener[0];
        if (changeSupport != null) {
            listeners = changeSupport.getPropertyChangeListeners(propName);
        }
        return listeners;
    }

    public void firePropertyChange(String propName, Object oldValue, Object newValue) {
        if (changeSupport == null) {
            return;
        }
        changeSupport.firePropertyChange(propName, oldValue, newValue);
    }

    public void fireIndexedPropertyChange(String propName, int idx, Object oldValue, Object newValue) {
        if (changeSupport == null) {
            return;
        }
        changeSupport.fireIndexedPropertyChange(propName, idx, oldValue, newValue);
    }

    private void ensurePropertyChangeSupport() {
        if (changeSupport != null) {
            return;
        }
        synchronized (lock) {
            if (changeSupport == null) {
                changeSupport = new PropertyChangeSupport(this);
            }
        }
    }

    private void ensureTransponder() {
        if (transponder != null) {
            return;
        }
        synchronized (lock) {
            if (transponder == null) {
                transponder = new DataChangeTransponder();
            }
        }
    }

    @Override
    public String toString() {
        return userObject == null ? "" : userObject.toString();
    }

    /**
     * 数据改变转发器, 将数据的改变转换为节点改变并派发事件
     */
    private class DataChangeTransponder implements PropertyChangeListener {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            firePropertyChange("data." + evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
        }
    }
}
